package com.chj.comp.common.core.util;

import com.google.zxing.*;
import com.google.zxing.Result;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import sun.font.FontDesignMetrics;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author ：cuihui
 * @date ：Created in 2023/3/16
 * @description：二维码工具类
 * @modified By：
 */
@Slf4j
public class QRCodeUtil {
    //上传路径
    public static String uploadpath = "E:";

    //编码
    public static final String DEFAULTCHARSET = "UTF-8";

    //后缀名
    public static final String SUFFIX = "jpg";

    //文件夹名
    public static final String BIZPATH = "qrcode";

    //宽
    public static final int QRWIDTH = 300;

    //高
    public static final int QRHEIGHT = 300;

    //logo宽
    public static final int LOGOWIDTH = 50;

    //logo高
    public static final int LOGOHEIGHT = 50;

    //文字行高
    public static final int LINEHEIGHT = 50;

    //文字大小
    public static final int FONTSIZE = 30;

    //图片格式
    private static final String IMG_FORMAT = "JPEG";

    //base64 图片前缀
    private static final String BASE64_PRE = "data:image/jpg;base64,";

    //是否删除白边
    private static Boolean isDeleteWhite = true;

    /**
     * 生成文件二维码
     * @param content
     * @return
     */
    public static String generateFileQRCode(String content){
        String ctxPath = generateUploadPath();
        BufferedImage qrImage = createQRImage(DEFAULTCHARSET,content,QRWIDTH,QRHEIGHT);
        try {
            ImageIO.write(qrImage, SUFFIX, new File(ctxPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ctxPath;
    }

    /**
     * 生成带logo文件二维码
     * @param content
     * @return
     */
    public static String generateFileQRCodeWithLogo(String content){
        String ctxPath = generateUploadPath();
        BufferedImage qrImage = createQRImage(DEFAULTCHARSET,content,QRWIDTH,QRHEIGHT);
        insertLogoImage(qrImage);
        try {
            ImageIO.write(qrImage, SUFFIX, new File(ctxPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ctxPath;
    }

    /**
     * 生成带顶部说明的文件二维码
     * @param content
     * @return
     */
    public static String generateFileQRCodeWithUpDesc(String content, String upDesc){
        String ctxPath = generateUploadPath();
        BufferedImage qrImage = createQRImage(DEFAULTCHARSET, content, QRWIDTH, QRHEIGHT);
        BufferedImage resultImage = addUpFont(qrImage, upDesc);
        try {
            ImageIO.write(resultImage, SUFFIX, new File(ctxPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ctxPath;
    }

    /**
     * 生成带底部说明的文件二维码
     * @param content
     * @return
     */
    public static String generateFileQRCodeWithBottomDesc(String content, String bottomDesc){
        String ctxPath = generateUploadPath();
        BufferedImage qrImage = createQRImage(DEFAULTCHARSET,content,QRWIDTH,QRHEIGHT);
        BufferedImage resultImage = addBottomFont(qrImage, bottomDesc);
        try {
            ImageIO.write(resultImage, SUFFIX, new File(ctxPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ctxPath;
    }

    /**
     * 生成base64字符串二维码
     * @param content
     * @return String
     * @throws IOException
     */
    public static String generateStringQRCode(String content) throws IOException{
        BufferedImage qrImage = createQRImage(DEFAULTCHARSET, content, QRWIDTH, QRHEIGHT);

        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        //写入流中
        ImageIO.write(qrImage, IMG_FORMAT, byteStream);
        //转换成字节
        byte[] bytes = byteStream.toByteArray();
        //转换成base64串
        String base64 = Base64.getEncoder().encodeToString(bytes).trim();
        //删除 \r\n
        base64 = base64.replaceAll("\n", "").replaceAll("\r", "");

        return BASE64_PRE + base64;
    }



    /**
     * 解析二维码
     */
    public static String decodeQRCode(InputStream inputStream) throws Exception {
        BufferedImage image;
        image = ImageIO.read(inputStream);
        if (image == null) {
            return null;
        }
        BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Result result;
        Hashtable hints = new Hashtable();
        hints.put(DecodeHintType.CHARACTER_SET, DEFAULTCHARSET);
        result = new MultiFormatReader().decode(bitmap, hints);
        String resultStr = result.getText();
        return resultStr;
    }

    /**
     * 对已经生成好的二维码设置logo
     * @param source
     */
    private static void insertLogoImage(BufferedImage source){
        Image logo = compressLogo();
        int logoWidth = logo.getWidth(null);
        int logoHeight = logo.getHeight(null);
        Graphics2D graph = source.createGraphics();
        int qrWidth = source.getWidth();
        int qrHeight = source.getHeight();
        int x = (qrWidth - logoWidth) / 2;
        int y = (qrHeight - logoHeight) / 2;
        graph.drawImage(logo, x, y, logoWidth, logoHeight, null);
        Shape shape = new RoundRectangle2D.Float(x, y, logoWidth, logoHeight, 6, 6);
        graph.setStroke(new BasicStroke(3f));
        graph.draw(shape);
        graph.dispose();
    }

    /**
     * 缩小logo图片
     * @return
     */
    private static Image compressLogo(){
        File file = new File(uploadpath + File.separator + BIZPATH + File.separator + "logo.jpg");
        if(!file.exists()){
            log.error("logo文件不存在");
            throw new RuntimeException("logo文件不存在");
        }
        Image original = null;
        try {
            original = ImageIO.read(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        int width = original.getWidth(null);
        int height = original.getHeight(null);
        if (width > LOGOWIDTH) {
            width = LOGOWIDTH;
        }
        if (height > LOGOHEIGHT) {
            height = LOGOHEIGHT;
        }
        Image image = original.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = tag.getGraphics();
        // 绘制缩小后的图
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return image;
    }

    /**
     * 顶部增加说明文字
     * @param qrImage
     * @param text
     */
    private static BufferedImage addUpFont(BufferedImage qrImage, String text) {
        // 获取文字说明image
        BufferedImage textImage = textToImage(text, QRWIDTH, LINEHEIGHT, FONTSIZE);
        // 获取背景image
        BufferedImage bgImage = createBGImage(QRWIDTH, QRHEIGHT + LINEHEIGHT);
        Graphics2D bgGraphics = bgImage.createGraphics();
        // 绘制文字说明
        bgGraphics.drawImage(textImage, 0, 0, QRWIDTH, LINEHEIGHT, null);
        // 绘制二维码
        bgGraphics.drawImage(qrImage, 0, LINEHEIGHT, QRWIDTH, QRHEIGHT, null);
        bgGraphics.dispose();
        return bgImage;
    }

    /**
     * 增加底部的说明文字
     * @param qrImage 二维码
     * @param text 说明内容
     */
    private static BufferedImage addBottomFont(BufferedImage qrImage, String text) {
        // 获取文字说明image
        BufferedImage textImage = textToImage(text, QRWIDTH, LINEHEIGHT, FONTSIZE);
        // 获取背景image
        BufferedImage bgImage = createBGImage(QRWIDTH, QRHEIGHT + LINEHEIGHT);
        Graphics2D bgGraphics = bgImage.createGraphics();
        // 绘制二维码
        bgGraphics.drawImage(qrImage, 0, 0, QRWIDTH, QRHEIGHT, null);
        // 绘制文字说明
        bgGraphics.drawImage(textImage, 0, QRHEIGHT, QRWIDTH, LINEHEIGHT, null);
        bgGraphics.dispose();
        return bgImage;
    }

    /**
     * 文字说明生成image
     * @param str
     * @param width
     * @param height
     * @param fontSize 字体大小
     * @return
     */
    private static BufferedImage textToImage(String str, int width, int height,int fontSize) {
        BufferedImage textImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D)textImage.getGraphics();
        //开启文字抗锯齿
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2.setBackground(Color.orange);
        g2.clearRect(0, 0, width, height);
        g2.setPaint(Color.BLACK);
        FontRenderContext context = g2.getFontRenderContext();
        Font font = new Font("微软雅黑", Font.BOLD, fontSize);
        g2.setFont(font);
        LineMetrics lineMetrics = font.getLineMetrics(str, context);
        FontMetrics fontMetrics = FontDesignMetrics.getMetrics(font);
        float offset = (width - fontMetrics.stringWidth(str)) / 2;
        float y = (height + lineMetrics.getAscent() - lineMetrics.getDescent() - lineMetrics.getLeading()) / 2;
        g2.drawString(str, (int)offset, (int)y);
        return textImage;
    }

    /**
     * 创建纯色背景图
     * @param width
     * @param height
     * @return
     */
    private static BufferedImage createBGImage(int width, int height){
        //重新绘制图
        BufferedImage bgImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D bgGraphics = bgImage.createGraphics();
        bgGraphics.setColor(Color.WHITE);
        //填充整个屏幕
        bgGraphics.fillRect(0, 0, QRWIDTH, QRHEIGHT + LINEHEIGHT);
        // 消除画图锯齿
        bgGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        // 消除文字锯齿
        bgGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        bgGraphics.dispose();
        return bgImage;
    }

    /**
     * 创建二维码
     * @param charSet 编码方式
     * @param content 二维码内容
     * @param qrWidth 二维码长度
     * @param qrHeight 二维码高度
     * @return
     */
    private static BufferedImage createQRImage(String charSet, String content, int qrWidth, int qrHeight){
        Hashtable hints = new Hashtable();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, charSet);
        // 二维码内边距，设置为0不能去白边，需特殊处理
        hints.put(EncodeHintType.MARGIN, isDeleteWhite ? 1 : 0);
        BitMatrix bitMatrix = null;
        try {
            bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, qrWidth , qrHeight, hints);
            // 删除二维码白色边框
            if(isDeleteWhite){
                bitMatrix = deleteWhite(bitMatrix);
            }
        } catch (WriterException e) {
            e.printStackTrace();
        }
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage qrImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                qrImage.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        return qrImage;
    }

    /**
     * 删除白色边框
     *
     * @param matrix matrix
     * @return BitMatrix
     */
    private static BitMatrix deleteWhite(BitMatrix matrix) {
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] + 1;
        int resHeight = rec[3] + 1;

        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1])){
                    resMatrix.set(i, j);
                }
            }
        }
        return resMatrix;
    }

    /**
     * 校验文件夹是否存在，不存在则创建
     * @param destPath
     */
    private static void mkdirs(String destPath) {
        File file = new File(destPath);
        // 当文件夹不存在时，mkdirs会自动创建多层目录，区别于mkdir．(mkdir如果父目录不存在则会抛出异常)
        if (!file.exists() && !file.isDirectory()) {
            file.mkdirs();
        }
    }

    /**
     * 生成路径存储二维码图片路径
     * @return
     */
    private static String generateUploadPath() {
        String ctxPath = uploadpath + File.separator + BIZPATH;
        //判断文件夹是否存在
        mkdirs(ctxPath);
        //获取随机文件名
        String fileName = randomName();
        ctxPath += File.separator + fileName + "." + SUFFIX;

        return ctxPath;
    }

    /**
     * 生成随机名称
     * @return
     */
    private static String randomName() {
        String prefix = "qr";
        SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss");
        String random = String.format("%04d",new Random().nextInt(9999));
        String value = prefix + format.format(new Date()) + random;
        return value;
    }
}
